/* 
 * File:   Layer.hpp
 * Author: Ana Paula
 *
 * Created on 16 de Junho de 2011, 11:22
 */

#ifndef LAYER_HPP
#define	LAYER_HPP

#include <vector>
#include <boost/thread.hpp>
#include "Bits.h"

namespace ufpb {
    
    namespace project {

        namespace network {

                //using namespace boost;
                
                class Layer {

                        //Construtor
                         public:

                                static const int BUFFER = 50;
                                /**
                                 * Cria uma camada genérica.
                                 * @param configurations vetor contento parâmetros
                                 * de inicialização e configuração.
                                 */
                                Layer(std::vector<int>* configurations);
                                
                                //Destructor
                                virtual ~Layer();

                                /**
                                 * Seta a camada de baixo para esta camada.
                                 * @param layer Ponteiro para a camada de baixo
                                 */
                                void setDownLayer(Layer* layer);
                                
                                /**
                                 * Seta a camada de cima para esta camada.
                                 * @param layer Ponteiro para a camada de cima
                                 */
                                void setUpLayer(Layer* layer);

                                /**
                                 * Acessa à camada de baixo que está ligada à
                                 * esta camada.
                                 * @return Ponteiro para a camada de baixo.
                                 */
                                Layer* getDownLayer(void);

                                /**
                                 * Acessa à camada de cima que está ligada à
                                 * esta camada.
                                 * @return Ponteiro para a camada de cima.
                                 */
                                Layer* getUpLayer(void);

                                /**
                                 * Inticializa esta camada iniciando as threads
                                 * que serão usadas para as entregas e recebimentos
                                 * das mensagens. Esse método deve ser chamada quando
                                 * a camada estiver pronta para enviar e receber
                                 * mensagens. Se esse método for sobrescrito,
                                 * obrigatoriamente, a classe base deverá chamar
                                 * o metódo init da classe pai ou não poderá se
                                 * comunicar com as outras camadas.
                                 */
                                virtual void init(void);

                                /**
                                 * Chamar esse metodo quando quiser enviar "data" pra camada de baixo.
                                 * Esse método só deve ser chamado pela camada de cima.
                                 * Ex:
                                 * Quando uma camada acima (Aplicação) quiser enviar dados para camada
                                 * abaixo dela(Transporte), deverá usar o seguinte código.
                                 * <code>this->getDownLayer()->send(msg);</code>
                                 * @param data Dados a serem enviados.
                                 */
                                void send(Bits* data);

                                /**
                                 * Chamar esse metodo quando quiser enviar "data" pra camada de cima
                                 * Esse método só deverá ser chamado pela camada de baixo.
                                 * Ex:
                                 * Quando uma camada acima (Enlace) quiser enviar dados recebidos
                                 * para a camada acima dela (Rede), deverá usar o seguinte
                                 * código:
                                 * <code>this->getUpLayer()->receive(msg);</code>
                                 * @param data Dados recebidos que serão enviados
                                 * para a camada de cima.
                                 */
                                void receive(Bits* data);

                                /**
                                 * Função responsável pela Thread de envio de dados.
                                 * Será chamada quando a Thread de envio for criada
                                 * chamando o método init
                                 * @param m
                                 */
                                friend void funcThreadEnvia(Layer* layer);
                                
                                /**
                                 * Função responsável pela Thread de recebimento de dados.
                                 * Será chamada quando a Thread de recebimento for criada
                                 * chamando o método init
                                 * @param m
                                 */
                                friend void funcThreadRecebe(Layer* layer);
                                
                        protected:

                                /**
                                 * Esse método será chamado quando a camada de cima
                                 * quiser enviar algum dado. Esse método é chamado
                                 * automaticamente pela Thread de envio quando houver
                                 * alguma mensagem na fila de envio. A mensagem
                                 * é retirada da fila e passada para esse função.
                                 * @param m Mensagem a ser enviada
                                 */
				virtual void newMessageToSend(Bits* m) = 0;
                                /**
                                 * Esse método será chamado quando a camada de baixo
                                 * quiser enviar algum dado recebido. Esse método é chamado
                                 * automaticamente pela Thread de recebimento quando houver
                                 * alguma mensagem na fila de recebimento. A mensagem
                                 * é retirada da fila e passada para esse função.
                                 * @param m Mensagem que foi recebida
                                 */
				virtual void newReceivedMessage(Bits* m) = 0;
                        
                        private:
                                std::vector<int>* config;
                                Layer* layerUp;
                                Layer* layerDown;
                                std::list<Bits*>* sendBuffer;
                                std::list<Bits*>* receiveBuffer;

                                boost::thread* sendt;
                                boost::thread* receivet;
	
				boost::mutex* datar;
				boost::condition_variable* cvr;

				boost::mutex* datas;
				boost::condition_variable* cvs;

                                boost::mutex* upMutex;
                                boost::mutex* downMutex;
                                //boost::t
                                                  
                };

            }
        }
}

#endif	/* LAYER_HPP */

